﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Net;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Controls;
using Common.Tool.DataTools;
using Serilog;
using Common.Tool;

namespace Common.Communication
{
    /// <summary>
    /// Socket基类
    /// </summary>
    public abstract class SocketClientBase :LogBase
    {
        /// <summary>
        /// 连接状态
        /// </summary>
        public virtual bool IsConnected => socket?.Connected ?? false;
        /// <summary>
        /// 分批缓冲区大小
        /// </summary>
        protected const int BufferSize = 4096;
        /// <summary>
        /// Socket实例
        /// </summary>
        protected Socket socket;
        /// <summary>
        /// 读写，连接时超时
        /// </summary>
        protected int timeout = -1;
        /// <summary>
        /// 连接的ip，端口
        /// </summary>
        protected IPEndPoint ipEndPoint;
        /// <summary>
        /// 设置连接对象
        /// </summary>
        /// <param name="ip"></param>
        /// <param name="port"></param>
        /// <param name="timeout"></param>
        /// <param name="isAutoOpen"></param>
        public SocketClientBase(string ip, int port, int timeout = -1)
        {
            if (ip == null)
            {
                return;
            }
            if (!IPAddress.TryParse(ip, out IPAddress address))
            {
                address = Dns.GetHostEntry(ip).AddressList?.FirstOrDefault();
            }
            lock (ipEndPoint)
            {
                ipEndPoint = new IPEndPoint(address, port);
            }
            this.timeout = timeout;
        }
        /// <summary>
        /// 连接（如果已经是连接状态会先关闭再打开）
        /// </summary>
        /// <returns></returns>
        public virtual Result Open()
        {
            var result = new Result();
            Close();
            try
            {
                socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                socket.ReceiveTimeout = timeout;
                socket.SendTimeout = timeout;
                AppendCommunication(ipEndPoint?.ToString(), "建立连接");
                //连接
                IAsyncResult connectResult = socket.BeginConnect(ipEndPoint, null, null);
                //阻塞当前线程           
                if (!connectResult.AsyncWaitHandle.WaitOne(timeout))
                    throw new TimeoutException("连接超时");
                socket.EndConnect(connectResult);
            }
            catch (Exception ex)
            {
                AppendErr(ipEndPoint?.ToString(), "建立连接失败", ex);
                Close();
                result.IsSucceed = false;
                result.Err = ex.Message;
                result.ErrCode = 408;
                result.Exception = ex;
            }
            return result.EndTime();
        }

        /// <summary>
        /// 关闭连接
        /// </summary>
        /// <returns></returns>
        public virtual Result Close()
        {
            Result result = new Result();
            try
            {
                if (socket?.Connected == true)
                {
                    AppendCommunication(ipEndPoint?.ToString(), "断开连接");
                    socket?.Shutdown(SocketShutdown.Both);//正常关闭连接               
                }
            }
            catch { }
            try
            {
                socket?.Close();
            }
            catch { }
            return result.EndTime();
        }
        /// <summary>
        /// 发送
        /// </summary>
        /// <returns></returns>
        protected virtual Result Write(byte[] command)
        {
            var result = new Result();
            try
            {
                if (socket?.Connected == false)
                {
                    var connectResult = Open();
                    if (!connectResult.IsSucceed)
                    {
                        return new Result<byte[]>(connectResult);
                    }
                }
                result.Requst = DataConvert.ByteArrayToString(command);
                AppendCommunication(ipEndPoint?.ToString(), "->"+ result.Requst);
                //发送命令
                socket.Send(command);
            }
            catch (Exception ex)
            {
                AppendErr(ipEndPoint?.ToString(), "发送失败", ex);
                result.IsSucceed = false;
                result.Err = ex.Message;
            }
            return result.EndTime();
        }
        /// <summary>
        /// Socket读取
        /// </summary>
        /// <param name="socket">socket</param>
        /// <param name="receiveCount">读取长度</param>          
        /// <returns></returns>
        protected Result<byte[]> Read(int receiveCount)
        {
            if (socket?.Connected == false)
            {
                var connectResult = Open();
                if (!connectResult.IsSucceed)
                {
                    return new Result<byte[]>(connectResult);
                }
            }
            var result = new Result<byte[]>();
            try
            {
                byte[] receiveBytes = new byte[receiveCount];
                int receiveFinish = 0;
                while (receiveFinish < receiveCount)
                {
                    // 分批读取
                    int receiveLength = (receiveCount - receiveFinish) >= BufferSize ? BufferSize : (receiveCount - receiveFinish);
                    var readLeng = socket.Receive(receiveBytes, receiveFinish, receiveLength, SocketFlags.None);
                    if (readLeng == 0)
                    {
                        Close();
                        result.IsSucceed = false;
                        result.Err = $"连接被断开";
                        result.AddErr2List();
                        return result;
                    }
                    receiveFinish += readLeng;
                }
                if (receiveBytes.Length == 0)
                {
                    result.IsSucceed = false;
                    result.Err = "返回超时";
                    AppendCommunication(ipEndPoint?.ToString(), result.Err);
                    return result.EndTime();
                }
                result.Value = receiveBytes;
                result.Response = DataConvert.ByteArrayToString(receiveBytes);
                AppendCommunication(ipEndPoint?.ToString(), "<-" + result.Response);
                return result.EndTime();
            }
            catch (Exception ex)
            {
                AppendErr(ipEndPoint?.ToString(), "读取返回失败", ex);
                result.IsSucceed = false;
                result.Err = ex.Message;
                result.AddErr2List();
                return result.EndTime();
            }
        }

        /// <summary>
        /// Socket读取
        /// </summary>
        protected Result<byte[]> ReadBytes()
        {
            if (socket?.Connected == false)
            {
                var connectResult = Open();
                if (!connectResult.IsSucceed)
                {
                    return new Result<byte[]>(connectResult);
                }
            }
            Result<byte[]> result = new Result<byte[]>();
            try
            {
                DateTime beginTime = DateTime.Now;
                var tempBufferLength = socket.Available;
                //在(没有取到数据或BytesToRead在继续读取)且没有超时的情况，延时处理
                while ((socket.Available == 0 || tempBufferLength != socket.Available) && DateTime.Now - beginTime <= TimeSpan.FromMilliseconds(socket.ReceiveTimeout))
                {
                    tempBufferLength = socket.Available;
                    //延时处理
                    Thread.Sleep(20);
                }
                byte[] buffer = new byte[socket.Available];
                var receiveFinish = 0;
                while (receiveFinish < buffer.Length)
                {
                    var readLeng = socket.Receive(buffer, buffer.Length, SocketFlags.None);
                    if (readLeng == 0)
                    {
                        result.IsSucceed = false;
                        result.Err = "连接被断开";
                        AppendCommunication(ipEndPoint?.ToString(), result.Err);
                        return result.EndTime();
                    }
                    receiveFinish += readLeng;
                }
                if (buffer.Length == 0)
                {
                    result.IsSucceed = false;
                    result.Err = "返回超时";
                    AppendCommunication(ipEndPoint?.ToString(), result.Err);
                    return result.EndTime();
                }
                result.Value = buffer;
                result.Response = DataConvert.ByteArrayToString(buffer);
                AppendCommunication(ipEndPoint?.ToString(), "<-" + result.Response);
                return result.EndTime();
            }
            catch (Exception ex)
            {
                AppendErr(ipEndPoint?.ToString(), "读取返回失败", ex);
                result.IsSucceed = false;
                result.Err = ex.Message;
                result.AddErr2List();
                return result.EndTime();
            }
        }

        /// <summary>
        /// 发送报文，并获取响应报文（如果网络异常，会自动进行一次重试）
        /// </summary>
        /// <param name="command"></param>
        /// <returns></returns>
        public virtual Result<byte[]> WriteThenRead(byte[] command, int wait = 10, bool tryagain = true)
        {
            Result<byte[]> result = new Result<byte[]>();
            Result<byte[]> _writeThenRead()
            {
                try
                {
                    //发送命令
                    var re = Write(command);
                    if (!re.IsSucceed)
                    {
                        return new Result<byte[]>(re, result.InitialTime);
                    }
                    Thread.Sleep(wait);
                    var re2 = ReadBytes();
                    re2.Requst = re.Requst;
                    //获取响应报文
                    return new Result<byte[]>(re2, result.InitialTime);
                }
                catch (Exception ex)
                {
                    result.IsSucceed = false;
                    result.Err = ex.Message;
                    result.AddErr2List();
                    return result.EndTime();
                }
            }
            lock (this)
            {
                result = _writeThenRead();
                if (!result.IsSucceed && tryagain)
                {
                    Thread.Sleep(100);
                    result = _writeThenRead();
                }
                return result;
            }
        }

        /// <summary>
        /// 发送报文，并获取响应报文（如果网络异常，会自动进行一次重试）
        /// </summary>
        /// <param name="command"></param>
        /// <returns></returns>
        public virtual Result<byte[]> WriteThenReadByLen(byte[] command, int receiveCount, int wait = 10, bool tryagain = true)
        {
            Result<byte[]> result = new Result<byte[]>();
            Result<byte[]> _writeThenRead()
            {
                try
                {
                    //发送命令
                    var re = Write(command);
                    if (!re.IsSucceed)
                    {
                        return new Result<byte[]>(re, result.InitialTime);
                    }
                    Thread.Sleep(wait);
                    var re2 = Read(receiveCount);
                    re2.Requst = re.Requst;
                    //获取响应报文
                    return new Result<byte[]>(re2, result.InitialTime);
                }
                catch (Exception ex)
                {
                    result.IsSucceed = false;
                    result.Err = ex.Message;
                    result.AddErr2List();
                    return result.EndTime();
                }
            }
            lock (this)
            {
                result = _writeThenRead();
                if (!result.IsSucceed && tryagain)
                {
                    Thread.Sleep(100);
                    result = _writeThenRead();
                }
                return result;
            }
        }
        /// <summary>
        /// 发送
        /// </summary>
        /// <returns></returns>
        protected virtual Result Write(string command)
        {
            return Write(Encoding.UTF8.GetBytes(command));
        }
        /// <summary>
        /// 读取
        /// </summary>
        /// <returns></returns>
        protected virtual Result<string> ReadString()
        {
            var info = ReadBytes();
            if (info.IsSucceed)
                return new Result<string>(info, Encoding.UTF8.GetString(info.Value));
            return new Result<string>(info);
        }
        /// <summary>
        /// 发送报文，并获取响应报文
        /// </summary>
        /// <param name="command"></param>
        /// <returns></returns>
        protected virtual Result<string> WriteThenRead(string command, int wait = 50, bool tryagain = false)
        {
            var info = WriteThenRead(Encoding.UTF8.GetBytes(command), wait, tryagain);
            if (info.IsSucceed)
                return new Result<string>(info, Encoding.UTF8.GetString(info.Value));
            return new Result<string>(info);
        }
    }
}
